package com.seadragon.app.ksr.controller;

import java.beans.PropertyEditorSupport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.gson.GsonBuilder;
import com.seadragon.app.ksr.model.Address;
import com.seadragon.app.ksr.model.AddressType;
import com.seadragon.app.ksr.model.Customer;
import com.seadragon.app.ksr.model.Office;
import com.seadragon.app.ksr.model.SearchParams;
import com.seadragon.app.ksr.model.SearchResult;
import com.seadragon.app.ksr.model.tiger.Location;
import com.seadragon.app.ksr.model.tiger.LocationFactory;
import com.seadragon.app.ksr.service.ReviewService;
import com.seadragon.app.ksr.service.SearchService;
import com.seadragon.app.ksr.util.CacheManager;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Polygon;

@Controller
public class SearchController {
	private static Logger logger = LoggerFactory.getLogger(SearchController.class);
	@Autowired
	private SearchService searchService;
	
	@Autowired
	private CacheManager cacheManager;

	@Autowired
	private ReviewService reviewService;
	@ModelAttribute
	public void populateModel(Model model) {
		model.addAttribute("categoryList", cacheManager.getAllLeafCatogories());
	}	

	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(List.class, "neighbors", new CustomCollectionEditor(List.class) {
			@Override
			protected Object convertElement(Object element) {
				Location location = null;
				if (element instanceof String && StringUtils.isNotEmpty((String) element)) {
					location = StringToLocation((String)element);
				}
				return location;
			}
		});
		binder.registerCustomEditor(Location.class,"targetLocation", new PropertyEditorSupport(){
			@Override
			public void setAsText(String text) throws IllegalArgumentException {
				if (StringUtils.isNotEmpty(text)) {
					setValue(StringToLocation(text));
				}
			}
		});
	}
	private Location StringToLocation(String s){
		Location location = null;
		if (StringUtils.isNotEmpty(s)) {
			String name=null;
			String type=null;
			String state=null;
			Double lat=null;
			Double lon=null;
			String key = null;
			String value = null;
			//format: name=xxx|type=xxx|state=xxx|lat=xxx|lon=xxx
			StringTokenizer token = new StringTokenizer(s, "|");
			StringTokenizer tk = null;
			try {
				while (token.hasMoreTokens()) {
					tk = new StringTokenizer(token.nextToken(), "=");
					key =  tk.hasMoreTokens()?tk.nextToken():null;
					value =  tk.hasMoreTokens()?tk.nextToken():null;
					if (StringUtils.isNotEmpty(key)&&StringUtils.isNotEmpty(value)){
						switch (key) { 
							case "name": 
								name = value; break; 
							case "type": 
								type = value; break; 
							case "state": 
								state = value; break; 
							case "lat": 
								lat = Double.valueOf(value); break; 
							case "lon": 
								lon = Double.valueOf(value); break; 
							default: 
								break;
						}
					}
				}
				location = LocationFactory.getInstance(name, AddressType.valueOf(type), state,lat,lon);
			} catch (IllegalArgumentException e) {
				logger.error("Failed to create Location because of wrong AddressType or lat/lon", e);
			} 
		}
		return location;
	}
	@RequestMapping(value="/reindex.html", method = RequestMethod.GET)
	public String reindex(HttpServletRequest request, HttpSession session){
		searchService.reindexOffice();
		searchService.reindexAddress();
		return "customer.search";
	}
	
   @RequestMapping(value="/search.html", method = RequestMethod.GET)
	public String show(HttpServletRequest request,@ModelAttribute("params") SearchParams params,Model model){
	   SearchResult searchResult = searchService.search(params);
	   model.addAttribute("searchResult", searchResult);
	   if (searchResult.getOffices()!=null&&searchResult.getOffices().size()>0){
		   model.addAttribute("latLngListJson", new GsonBuilder().serializeNulls().create().toJson(getLatLngList(searchResult.getOffices())));
	   } else if (searchResult.getCustomers()!=null&&searchResult.getCustomers().size()>0){
		   model.addAttribute("latLngListJson", new GsonBuilder().serializeNulls().create().toJson(getLatLngList(searchResult.getCustomers())));
	   } else {
		   //put dummy customer data:
		   searchResult.getCustomers().add(DummyData.getCustomer());
		   model.addAttribute("latLngListJson", new GsonBuilder().serializeNulls().create().toJson(getLatLngList(searchResult.getCustomers())));
	   }
	   
	   if (searchResult.getTargetLocation()!=null&&searchResult.getTargetLocation().getGeom()!=null){
		   List<Location> targetList = new ArrayList<>();
		   targetList.add(searchResult.getTargetLocation());
		   model.addAttribute("targetMultiPolygonJson", getMultiPolygonJson(targetList));
	   }
	   if (searchResult.getLocationFilter()!=null&&searchResult.getLocationFilter().size()>0){
		   model.addAttribute("neighborMultiPolygonJson", getMultiPolygonJson(searchResult.getLocationFilter()));
	   }
	   
	   model.addAttribute("searchParams", params);
	   if ("customer".equalsIgnoreCase(params.getSearchType()))
		   return "business.search";
	   else 
		   return "customer.search";
	}
   
	private String getMultiPolygonJson(List<? extends Location> list) {
		String result = null;
		Map<String,Object> polygonMap = null;
			polygonMap = new HashMap<String,Object>();
			for (Location place : list) {
				if (place.getGeom()!=null&&place.getGeom().getCoordinates()!=null&&!place.getGeom().isEmpty()){
					List<Coordinate[]> polygonlist = new ArrayList<>();
					for (int i = 0; i < place.getGeom().getNumGeometries(); i++) {
						Object polygon = place.getGeom().getGeometryN(i);
						if (polygon!=null&&polygon instanceof Polygon){
							Polygon pg = (Polygon)polygon;
							polygonlist.add(pg.getExteriorRing().getCoordinates());
							if (pg.getNumInteriorRing()>0){
								for (int j = 0; j < pg.getNumInteriorRing(); j++) {
									polygonlist.add(pg.getInteriorRingN(j).getCoordinates());
								}
							}
						}
					}
					polygonMap.put(place.getName(), polygonlist);
				}
			}
		result = new GsonBuilder().serializeSpecialFloatingPointValues().create().toJson(polygonMap);
		return result;
	}
   private List<Map<String,Object>> getLatLngList(List<?> olist) {
		List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
		Map<String,Object> result = null;
		for (Object o:olist) {
				result = new HashMap<String, Object>();
				if (o instanceof Office){
					result.put("lat",((Office)o).getAddress().getLatitude() );
					result.put("lng",((Office)o).getAddress().getLongitude() );
				} else if (o instanceof Customer){
					result.put("lat",((Customer)o).getResidentAddress().getLatitude() );
					result.put("lng",((Customer)o).getResidentAddress().getLongitude() );
				}
				list.add(result);
		}
		return list;
	}  
}

